﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using xKnight.Analyzing.Csrf.Model;
using xKnight.Data;
using xKnight.Models;

namespace xKnight.Analyzing.Csrf
{
    internal class CsrfAnalyzer : Analyzer
    {
        #region Properties

        protected CsrfAnalyzingSharedResource SharedResource
        {
            get
            {
                return _analyzingSharedResource as CsrfAnalyzingSharedResource;
            }
        }

        #endregion
    
        #region Constructors

        internal CsrfAnalyzer(Attack attack)
            :base(attack)
        {
            
        }

        #endregion

        #region Interface

        public override void Analyze()
        {
            CsrfAttack csrfAttack=null;
            while ((csrfAttack = GetNotAnalyzedAttack()) != null)
            {
                DoAnalyze(csrfAttack);
            }
        }

        protected override AnalyzingSharedResource CreateAnalyzingSharedResource(Attack attack)
        {
            object sharedLock = new object();

            CsrfAttack[] attacks = DataLayer.GetCsrfAttacks(attack);
            Queue<CsrfAttack> sharedQueue = new Queue<CsrfAttack>();

            for (int i = 0; i < attacks.Length; i++)
                sharedQueue.Enqueue(attacks[i]);

            CsrfAnalyzingSharedResource sharedResource = new CsrfAnalyzingSharedResource(sharedQueue, sharedLock, attack);

            return sharedResource;
        }

        #endregion

        #region Private Methods
        private void DoAnalyze(CsrfAttack attack)
        {
            Form[] oldForms = DataLayer.GetCsrfAttackOldForms(attack);

            CompareFormSets(attack.CsrfAttackForms.ToArray(),oldForms);

            DataLayer.Save(attack);

            CsrfAnalyzeAnnounceItem announceItem = new CsrfAnalyzeAnnounceItem(attack,"Analyzed Done",DateTime.Now);
            OnAnalyzeAnnounced(announceItem);
        }

        private void CompareFormSets(CsrfAttackForm[] CsrfAttackForms, Form[] oldForms)
        {
            for (int newIndex = 0; newIndex < CsrfAttackForms.Length; newIndex++)
            {
                for (int oldIndex = 0; oldIndex < oldForms.Length; oldIndex++)
                {
                    Form newForm = CsrfAttackForms[newIndex].Form;
                    Form oldForm = oldForms[oldIndex];

                    if (newForm.GetId() == oldForm.GetId())
                    {
                        string[] tokens = FindTokens(newForm, oldForm);

                        AddTokensToCsrfAttackForm(CsrfAttackForms[newIndex], tokens);
                    }
                }
            }
        }

        private void AddTokensToCsrfAttackForm(CsrfAttackForm csrfAttackForm, string[] tokens)
        {
            if (tokens.Length == 0)
                csrfAttackForm.CsrfAttack.Succeed = true;
            else
                csrfAttackForm.CsrfAttack.Succeed = false;

            DataLayer.Save(csrfAttackForm.CsrfAttack);

            for (int i = 0; i < tokens.Length; i++)
            {
                CsrfToken csrfToken = new CsrfToken();
                csrfToken.CsrfAttackFormId = csrfAttackForm.Id;
                csrfToken.Name = tokens[i];

                DataLayer.Save(csrfToken);
            }
        }

        private string[] FindTokens(Form newForm, Form oldForm)
        {
            List<string> tokens = new List<string>();

            for (int newIndex = 0; newIndex < newForm.FormElements.Count; newIndex++)
            {
                for (int oldIndex = 0; oldIndex < oldForm.FormElements.Count; oldIndex++)
                {
                    FormElement newFormElement = newForm.FormElements.ElementAt(newIndex);
                    FormElement oldFormElement = newForm.FormElements.ElementAt(oldIndex);

                    if (newFormElement.Type == "input/hidden"
                        && newFormElement.Name == oldFormElement.Name)
                    {
                        if (newFormElement.Value != oldFormElement.Value)
                            tokens.Add(newFormElement.Name);
                        break;
                    }
                }
            }

            return tokens.ToArray();
        }

        protected virtual CsrfAttack GetNotAnalyzedAttack()
        {
            CsrfAttack xAttack = null;

            lock (SharedResource.SharedLock)
            {
                xAttack = SharedResource.SharedQueue.Count != 0 ? SharedResource.SharedQueue.Dequeue() : null;
            }

            return xAttack;
        }

        #endregion
    }
}
